package alexico;

import generacionCodigo.Assembler;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import util.FileUtils;
import alexico.operacion.Operacion01;
import alexico.operacion.Operacion02;
import alexico.operacion.Operacion03;
import alexico.operacion.Operacion04;
import alexico.operacion.Operacion05;
import alexico.operacion.Operacion06;
import alexico.operacion.Operacion07;
import alexico.operacion.Operacion08;
import alexico.operacion.Operacion09;
import alexico.operacion.Operacion10;
import alexico.operacion.Operacion11;
import alexico.operacion.Operacion12;
import alexico.operacion.Operacion14;
import alexico.operacion.OperacionE13;
import codigoIntermedio.Terceto;

public class AnalizadorLexico {

	private NodoMatriz[] defaultNodes;
	private Matriz m;
	private String codigo;
	private int posLinea;
	private int posCaracter;
	private int estadoFinal = 14;
	private File warnings;
	private static File errores;
	private static File tablaDeSimbolos;
	private static File tercetos;
	//private File tokens;
	//private File msjSintacticos;
	private static ArrayList<ErrorContainer> listaErrores;
	
	private static String fileID;

	public AnalizadorLexico(String codigo) {
		this.codigo = codigo;
		posLinea = 1;
		posCaracter = 0;
		inicializarAnalizador();
		warnings = new File(fileID + "-warnings.txt");
		errores = new File(fileID + "-errores.txt");
		tablaDeSimbolos = new File(fileID + "-tablaDeSimbolos.txt");
		//tokens = new File(fileID + "-tokens.txt");
		//msjSintacticos = new File(fileID + "-msjSintacticos.txt");
		tercetos = new File(fileID + "-tercetos.txt");

		if (warnings.exists()) {warnings.delete();}
		if (errores.exists()) {errores.delete();}
		if (tablaDeSimbolos.exists()) {tablaDeSimbolos.delete();}
		//if (tokens.exists()) {tokens.delete();}
		//if (msjSintacticos.exists()) {msjSintacticos.delete();}
		if (tercetos.exists()) {tercetos.delete();}
	}

	private void inicializarAnalizador() {
		defaultNodes = new NodoMatriz[14];
		m = new Matriz(14, 13);
		generarDefaultNodes();
		generarMatriz();
		listaErrores=new ArrayList<>();
	}

	public Token getNextToken() {
		Token t = new Token();
		if (posCaracter >= codigo.length()) {
			t.setTipo(TokenType.FIN_ARCHIVO);
			return t;
		}
		t.setLinea(posLinea);
		int estado = 0;
		while (estado != estadoFinal && posCaracter < codigo.length()) {
			char c = codigo.charAt(posCaracter);
			NodoMatriz n = m.getNodoMatriz(estado, codigo.charAt(posCaracter));
			if (n == null) {
				n = defaultNodes[estado];
			}
			try {
				estado = n.getProximo(c, t, this);
			} catch (Exception e) {
				e.printStackTrace();
				estado = estadoFinal;
			}
			if (c == '\n') {
				posLinea++;
			}
			posCaracter++;
		}
		if (new String().equals(t.getCadena())) {
			t.setTipo(TokenType.FIN_ARCHIVO);
		}
		return t;
	}

	public void volver() {
		posCaracter--;
		if (codigo.charAt(posCaracter) == '\n') {
			posLinea--;
		}
	}

	private void generarDefaultNodes() {
		defaultNodes[0] = new NodoMatriz(new Operacion01(), estadoFinal);
		defaultNodes[1] = new NodoMatriz(new Operacion03(), estadoFinal);
		defaultNodes[2] = new NodoMatriz(new Operacion04(), estadoFinal);
		defaultNodes[3] = new NodoMatriz(new OperacionE13(), estadoFinal);
		defaultNodes[4] = new NodoMatriz(new Operacion01(), 4);
		defaultNodes[5] = new NodoMatriz(new Operacion06(), estadoFinal);
		defaultNodes[6] = new NodoMatriz(new Operacion07(), estadoFinal);
		defaultNodes[7] = new NodoMatriz(new Operacion07(), estadoFinal);
		defaultNodes[8] = new NodoMatriz(new Operacion02(), 8);
		defaultNodes[9] = new NodoMatriz(new Operacion09(), estadoFinal);
		defaultNodes[10] = new NodoMatriz(new Operacion10(), estadoFinal);
		defaultNodes[11] = new NodoMatriz(new Operacion14(), estadoFinal);
		defaultNodes[12] = new NodoMatriz(new OperacionE13(), estadoFinal);
		defaultNodes[13] = new NodoMatriz(new Operacion11(), estadoFinal);
	}

	private void generarMatriz() {
		m.setNodoMatriz(0, 0, new NodoMatriz(new Operacion01(), 0));
		m.setNodoMatriz(0, 1, new NodoMatriz(new Operacion01(), 0));
		m.setNodoMatriz(0, 2, new NodoMatriz(new Operacion02(), 1));
		m.setNodoMatriz(0, 3, new NodoMatriz(new Operacion02(), 2));
		m.setNodoMatriz(0, 4, new NodoMatriz(new Operacion05(), estadoFinal));
		m.setNodoMatriz(0, 5, new NodoMatriz(new Operacion02(), 3));
		m.setNodoMatriz(0, 6, new NodoMatriz(new Operacion02(), 5));
		m.setNodoMatriz(0, 7, new NodoMatriz(new Operacion02(), 6));
		m.setNodoMatriz(0, 8, new NodoMatriz(new Operacion02(), 7));
		m.setNodoMatriz(0, 9, new NodoMatriz(new Operacion01(), 8));
		m.setNodoMatriz(0, 10, new NodoMatriz(new Operacion02(), 12));
		m.setNodoMatriz(0, 11, new NodoMatriz(new Operacion05(), estadoFinal));
		m.setNodoMatriz(0, 12, new NodoMatriz(new Operacion01(), 0));

		m.setNodoMatriz(1, 2, new NodoMatriz(new Operacion02(), 1));
		m.setNodoMatriz(1, 3, new NodoMatriz(new Operacion02(), 1));

		m.setNodoMatriz(2, 3, new NodoMatriz(new Operacion02(), 2));

		m.setNodoMatriz(3, 5, new NodoMatriz(new Operacion14(), 4));

		m.setNodoMatriz(4, 1, new NodoMatriz(new Operacion01(), 0));

		m.setNodoMatriz(5, 6, new NodoMatriz(new Operacion05(), estadoFinal));

		m.setNodoMatriz(6, 6, new NodoMatriz(new Operacion08(), estadoFinal));

		m.setNodoMatriz(7, 6, new NodoMatriz(new Operacion08(), estadoFinal));

		m.setNodoMatriz(8, 9, new NodoMatriz(new Operacion01(), 9));//TODO
		m.setNodoMatriz(8, 11, new NodoMatriz(new Operacion01(), 10));

		m.setNodoMatriz(10, 12, new NodoMatriz(new Operacion01(), 13));

		m.setNodoMatriz(11, 9, new NodoMatriz(new Operacion01(), 8));

		m.setNodoMatriz(12, 6, new NodoMatriz(new Operacion12(), estadoFinal));

		m.setNodoMatriz(13, 1, new NodoMatriz(new Operacion01(), 8));
	}

	public void agregarWarning(String texto) {
		FileUtils.writeLine(warnings, texto);
	}

	public void agregarError(String texto, Token t) {
		//
		listaErrores.add(new ErrorContainer(t, texto));
	}

	public void armarYEnviarError(Token t, String error) {
		String texto = "ERROR SINTACTICO: Se produjo un error al intentar procesar la linea "
				+ t.getLinea()
				+ ". Descripcion del error: " + error;
		this.agregarError(texto, t);
	}

	public void agregarToken(Token t) {
		//FileUtils.writeLine(tokens, t.getTipo() + ": " + t.getCadena());
	}

	public void agregarNuevoMensajeSintactico(String texto, int linea) {
		//String aux = texto + " en la linea " + linea + ".";
		//FileUtils.writeLine(msjSintacticos, aux);
	}

	public static void main(String[] args) {
		AnalizadorLexico.fileID = args[0];
		if (args.length == 1) {
			if (new Parser(FileUtils.loadFile(args[0])).yyparse() == 0) {
				System.out.println("Codigo correcto.");
			} else {
				System.out.println("Codigo incorrecto. El parser finalizo con valor '1'.");
			}
		} else {
			System.err.println("Cantidad de parametros incorrecta. Se espera solo un parametro.");
		}
		FileUtils.pasarArchivo(TablaDeSimbolos.simbolos, tablaDeSimbolos);
		for (ErrorContainer e: listaErrores){
			if (e.gettError().isEs_error())
				FileUtils.writeLine(errores, e.getMsj());
		}
		
		for (String l : Terceto.getFunciones().keySet()){
			List<Terceto> tercetos = Terceto.getFunciones().get(l);
			FileUtils.writeLine(AnalizadorLexico.tercetos, "Function [" + l + "]");
			for (Terceto t : tercetos){
				FileUtils.writeLine(AnalizadorLexico.tercetos, t.toString());
			}
			FileUtils.writeLine(AnalizadorLexico.tercetos, "");
		}
		FileUtils.writeLine(tercetos, "Codigo del Programa");
		for (int i = 0 ; i < Terceto.getTercetos().size() ; i++){
			Terceto t = Terceto.getTercetos().get(i);
			FileUtils.writeLine(tercetos, t.toString());
		}		
		Assembler.generarCodigoAssembler();
	}
}